#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/version.h>
//++ Foster
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/fs.h>
#include <linux/kernel.h>
 //-- Foster
#include <linux/spinlock.h>

#include "i2c.h"
#include "fcap_ctrl.h"
#include "video_type.h"

#undef DEBUG
//#define DEBUG

extern char *cmos_7660_tokens[];
extern char *cmos_7640_tokens[];
extern char *cmos_MT9V111_tokens[];    //steven
extern char *cmos_9655_tokens[];    //steven
extern char *cmos_9655_vga_tokens[];    //steven
extern char *cmos_7710_tokens[];    //steven
extern char *cmos_7720_tokens[];    //steven
extern char *cmos_MT9M112_tokens[];
extern char *cmos_MT9M112_vga_tokens[];
extern char *sa7137_a11_cvbs_ntsc[];


spinlock_t   i2c_lock = SPIN_LOCK_UNLOCKED;;

#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0))
	int substring(char **ptr, char *string, char *pattern)
	{   
	    register int i;
	
	    ptr[0]=(char *)strsep(&string, pattern);
	    for (i=0;ptr[i]!=NULL;++i)
	    {   
	        ptr[i+1]=strsep(&string,pattern);
	    }
		return i;
	}
#else
	int substring(char **ptr, char *string, char *pattern)
	{   
	    register int i;
	
	    ptr[0]=(char *)strtok(string, pattern);
	    for (i=0;ptr[i]!=NULL;++i)
	    {   
	        ptr[i+1]=strtok(NULL,pattern);
	    }
		return i;
	}
#endif


/*
 *	For the COMS VO7640/VO7660
 */
int fcap_setComs(__u32 width,int input_type)
{
	int rdata, rdata2, ret = -1;

    if(input_type==-1)   //auto
    {
      rdata = 0;                                    //steven
      I2C_Write(0x90, 0x01, 0x00);
      I2C_Write(0x90, 0x7f, 0x04);
      rdata = I2C_Read(0x91,0x36);
      rdata2 = I2C_Read(0x91,0x7f);
      if(rdata == 0x82 && rdata2 == 0x3a)
        rdata = I2C_REG_MT9V111;
      else{
        rdata = I2C_Read(0x61,0x0b);
#ifdef million_pixel
        if ( (rdata != I2C_REG_OV9655) && (rdata != I2C_REG_OV7710) ){
#else
        if (rdata != I2C_REG_OV7710){
#endif
	        I2C_Write(0x90, 0xf0, 0x00);
    	    I2C_Write(0x90, 0xf1, 0x01);
	        rdata = I2C_Read(0x91,0x00);
	        if(rdata == I2C_REG_MT9M112)
	    	    rdata = I2C_REG_MT9M112;
          else{
            rdata = I2C_Read(0x43,0x0b);
            if (rdata != I2C_REG_OV7720)
              rdata &= 0xf0;
            
          }
        }
      }   // end of if(rdata == 0x82 && rdata2 == 0x3a)
  
        switch (rdata)
        {
            case I2C_REG_OV7660:
                if(RunProgram(cmos_7660_tokens) >= 0)
                    ret = INPUT_OV7660;
                break;
            case I2C_REG_OV7640:
                if(RunProgram(cmos_7640_tokens)>=0)
                    ret = INPUT_OV7640;
                break;
            case I2C_REG_MT9V111:
      	        if(RunProgram(cmos_MT9V111_tokens)>=0)
      	        {   //steven
                    printk("MT9V111 init finished\n");
    			    ret = INPUT_MT9V11;                         //steven
                }
                break;
            case I2C_REG_OV9655:
                if (width == 1280)
                {
      		        if(RunProgram(cmos_9655_tokens)>=0)
      		        {
                        printk("OV9655 init finished\n");
    		  	        ret = INPUT_OV9655;
                    }
                }
                else
                {
      		        if(RunProgram(cmos_9655_vga_tokens)>=0)
      		        {
                        printk("OV9655 vga init finished\n");
    		  	        ret = INPUT_OV9655;
                    }
                }
                break;
            case I2C_REG_OV7710:
                if(RunProgram(cmos_7710_tokens)>=0)
                    ret = INPUT_OV7710;
                break;
            case I2C_REG_OV7720:
                if(RunProgram(cmos_7720_tokens)>=0)
                    ret = INPUT_OV7720;
                break;
            case I2C_REG_MT9M112:
                if (width == 1280)
                {
      		    if(RunProgram(cmos_MT9M112_tokens)>=0)
      		    {
                        printk("MT9M112 init finished\n");
    		  	ret = INPUT_MT9M112;
                    }
                }
                else
                {
      		    if(RunProgram(cmos_MT9M112_vga_tokens)>=0)
      		    {
                        printk("MT9M112 vga init finished\n");
    		  	ret = INPUT_MT9M112;
                    }
                }
                break;                
        }        
	}
	else //fix set
	{
	    ret=input_type;
	    switch(input_type)
	    {
            case INPUT_OV7640:
                RunProgram(cmos_7640_tokens);
                break;
            case INPUT_OV7660:
                RunProgram(cmos_7660_tokens);
                break;
            case INPUT_OV7710:
                RunProgram(cmos_7710_tokens);
                break;
            case INPUT_OV7720:
                RunProgram(cmos_7720_tokens);
                break;
            case INPUT_MT9V11:
                RunProgram(cmos_MT9V111_tokens);
                break;
            case INPUT_OV9655:
                RunProgram(cmos_9655_vga_tokens);
                break;
            case INPUT_IPTV7137TUNER:
                RunProgram(sa7137_a11_cvbs_ntsc);
                break;
            case I2C_REG_MT9M112:
                RunProgram(cmos_MT9M112_vga_tokens);//justin
                break;            
	    }
	}   
	    
	return ret;
}

int ExecuteCommand(unsigned int tokenvalue[], int tokennum)
{
	int rdata;

	// read or write
	if(tokenvalue[0]&0x1)	// read
	{	
		// only support single read			
		///printk("command: %02x %02x\n", tokenvalue[0], tokenvalue[1]);
		if ((rdata = I2C_Read(tokenvalue[0],tokenvalue[1]))<0) {
			printk("command: %02x %02x failure\n", tokenvalue[0], tokenvalue[1]);
//			return -1;
		}
#ifdef DEBUG
		else {
			printk("command: %02x %02x %02x\n", tokenvalue[0], tokenvalue[1], rdata);
		}
#endif
	}
	else	// write
	{
		if(tokennum <= 3) // single write data
		{
			///printk("command: %02x %02x %02x\n", tokenvalue[0], tokenvalue[1], tokenvalue[2]);
			if (I2C_Write(tokenvalue[0],tokenvalue[1],tokenvalue[2])<0) {
				printk("command: %02x %02x %02x failure\n", tokenvalue[0], tokenvalue[1], tokenvalue[2]);
				return -1;
			}
#ifdef DEBUG
			else {
				printk("command: %02x %02x %02x\n", tokenvalue[0], tokenvalue[1], tokenvalue[2]);
			}
#endif
		}
		else	// multiple write data
		{
		}
	}
	return 0;
}

int Execute_saa7137_Command(unsigned int tokenvalue[], int tokennum)
{
	int rdata,i;
	char buf[10];

	// read or write
	if(tokenvalue[0]&0x1)	// read
	{	
		// only support single read			
		///printk("command: %02x %02x\n", tokenvalue[0], tokenvalue[1]);
		if ((rdata = I2C_Read(tokenvalue[0],tokenvalue[1]))<0) {
			printk("command: %02x %02x failure\n", tokenvalue[0], tokenvalue[1]);
//			return -1;
		}
	}
	else	// write
	{
		if(tokennum == 2) 
		{
			//printk("command: %02x %02x\n", tokenvalue[0], tokenvalue[1]);
			if (I2C_saa7137_Write(tokenvalue,tokennum)<0) {
				printk("command: %02x %02x failure\n", tokenvalue[0], tokenvalue[1]);
				return -1;
			}

		}
		else if(tokennum == 3) 
		{
			//printk("command: %02x %02x %02x\n", tokenvalue[0], tokenvalue[1], tokenvalue[2]);
			for(i=0; i<tokennum; i++)
				buf[i]=tokenvalue[i];
			if (I2C_saa7137_Write(buf,tokennum)<0) {
				printk("command: %02x %02x %02x failure\n", tokenvalue[0], tokenvalue[1], tokenvalue[2]);
				return -1;
			}

		}
		else if(tokennum == 4) 
		{
			//printk("command: %02x %02x %02x %02x\n", tokenvalue[0], tokenvalue[1], tokenvalue[2], tokenvalue[3]);
			for(i=0; i<tokennum; i++)
				buf[i]=tokenvalue[i];			
			if (I2C_saa7137_Write(buf,tokennum)<0) {
				printk("command: %02x %02x %02x %02x failure\n", tokenvalue[0], tokenvalue[1], tokenvalue[2], tokenvalue[3]);
				return -1;
			}

		}
		else if(tokennum == 5) 
		{
			//printk("command: %02x %02x %02x %02x %02x\n", tokenvalue[0], tokenvalue[1], tokenvalue[2], tokenvalue[3], tokenvalue[4]);
			for(i=0; i<tokennum; i++)
				buf[i]=tokenvalue[i];			
			if (I2C_saa7137_Write(buf,tokennum)<0) {
				printk("command: %02x %02x %02x %02x failure\n", tokenvalue[0], tokenvalue[1], tokenvalue[2], tokenvalue[3]);
				return -1;
			}

		}
		else if(tokennum == 6) 
		{
			//printk("command: %02x %02x %02x %02x %02x %02x\n", tokenvalue[0], tokenvalue[1], tokenvalue[2], tokenvalue[3], tokenvalue[4], tokenvalue[5]);
			for(i=0; i<tokennum; i++)
				buf[i]=tokenvalue[i];			
			if (I2C_saa7137_Write(buf,tokennum)<0) {
				printk("command: %02x %02x %02x %02x failure\n", tokenvalue[0], tokenvalue[1], tokenvalue[2], tokenvalue[3]);
				return -1;
			}

		}
		else
			printk("tokennum error = %d\n",tokennum);
	}
	return 0;
}


#define xtod(c)				((c) <= '9' ? (c) - '0' : (c) <= 'F' ? (c) - 'A' + 10 : (c) - 'a' + 10)


long
atoi(register char *p)
{
	register long n;
    register int c, neg = 0;

    if (p == NULL)
		return 0;

    if (!isdigit(c = *p)) 
    {
        while (isspace(c))
			c = *++p;
        switch (c) {
        	case '-':
                neg++;
        	case '+': /* fall-through */
                c = *++p;
        }
        if (!isdigit(c))
            return (0);
    }
    if (c == '0' && *(p + 1) == 'x') 
    {
        p += 2;
        c = *p;
        n = xtod(c);

        while ((c = *++p) && isxdigit(c)) 
        {
            n *= 16; /* two steps to avoid unnecessary overflow */
            n += xtod(c); /* accum neg to avoid surprises at MAX */
        }
    } 
    else 
    {
        n = '0' - c;
        while ((c = *++p) && isdigit(c)) 
        {
            n *= 10; /* two steps to avoid unnecessary overflow */
            n += '0' - c; /* accum neg to avoid surprises at MAX */
        }
    }
    return (neg ? -n : n);
}

int fcap_misc_init_I2C(void)
{
	if (I2C_Init() < 0) {
	   printk ("i2c init fail\n");
	   return -1;
	}
	return 0;
}

int RunProgram(char *sa_tokens[])
{
	unsigned int tokenvalue[30];
	int argc;
	char *argv[30];
	char buffer[256];
	int i;	
	int j;
	
//	printk("Start to programming video decoder...\n");
#if 0
	if (I2C_Init() < 0) {
	   printk ("i2c init fail\n");
	   return -1;
	}
#endif

	for (i=0; sa_tokens[i]!=0; ++i)
	{
		strcpy(buffer, sa_tokens[i]);
		argc = substring(argv, buffer, " r\n\t");
		
		if (argc > 0)	// has data
		{
			// change token[] to tokenvalue[]
			for (j=0; j<argc; ++j)
			{
				tokenvalue[j] = (unsigned char)atoi(argv[j]);
			}
			if (ExecuteCommand(tokenvalue, argc) < 0)
				return -1;
		}			
	}
	
	
//	printk("Done...\n");
	return 0;
}

int Run_sa7137(char *sa_tokens[])
{
	unsigned int tokenvalue[30];
	int argc;
	char *argv[30];
	char buffer[256];
	int i;	
	int j;
	unsigned long flags;

	static unsigned int start_f=0;

	if(start_f ==1)
		return 0;
	start_f = 1;
		
	printk("\nStart to programming SAA7137...\n");
	fLib_I2C_SetClockdiv(0x50);

	spin_lock_init(&i2c_lock);
	spin_lock_irqsave(&i2c_lock, flags);

	for (i=0; sa_tokens[i]!=0; ++i)
	{
		strcpy(buffer, sa_tokens[i]);
		argc = substring(argv, buffer, " r\n\t");
		
		if (argc > 0)	// has data
		{
			// change token[] to tokenvalue[]
			for (j=0; j<argc; ++j)
			{
				tokenvalue[j] = (unsigned char)atoi(argv[j]);
			}
			if (Execute_saa7137_Command(tokenvalue, argc) < 0)
			{
				spin_unlock_irqrestore( &i2c_lock, flags);
				printk("error..%d.\n",i);
				return -1;
			}
		}			
	}
	spin_unlock_irqrestore(&i2c_lock, flags);	
	printk("Done...\n");
	return 0;
}

int fcap_misc_run_7113_userdef(void)
{

	char		filename[]="/ffmpeg/7113", *fcap_misc_string;
	char		token_start[]="\"0",token_end[]="\",", *token_start_pt, *token_end_pt;
	char		token_check[]=" xXaAbBcCdDeEfF0123456789";
	int 		retval=-1, token_length, j;
	struct file	*filePoint= NULL;
	mm_segment_t old_fs;
	
	unsigned int tokenvalue[30];
	int argc;
	char *argv[30];
	char buffer[256];
	
 	filePoint = filp_open(filename, O_RDONLY, 0);
	if (IS_ERR(filePoint)) {
//printk("unable to open file: %s\n", filename);
		return -1;
	}
	if((filePoint->f_op->read==0)||(filePoint->f_op==0))
		goto fail1;
	fcap_misc_string = kmalloc(PAGE_SIZE, GFP_KERNEL);
	if (!fcap_misc_string)
	{
		printk("fcap_misc_run_7113: allocate memory failed!!\n");
		goto fail1;
	}
	memset(fcap_misc_string, 0, PAGE_SIZE);
	old_fs = get_fs();
	set_fs(get_ds());
	if((retval = filePoint->f_op->read(filePoint, fcap_misc_string, PAGE_SIZE, &filePoint->f_pos))<=0){
		printk("fcap_misc_run_7113: read file error!!\n");
		goto fail2;
	}
	if(strstr(fcap_misc_string, "7113_tokens")==NULL){
		printk("fcap_misc_run_7113: keyword <7113_tokens> cannot be found!!\n");
		goto fail2;
	}
	/* delete comment */
	while(token_start_pt = strstr(fcap_misc_string, "//")){
		token_end_pt = strstr(token_start_pt, "\n");
		memset(token_start_pt,' ',token_end_pt-token_start_pt);
	}
	printk(KERN_DEBUG "SAA7113 User command:\n", buffer);
 	/* parsing token */
	token_start_pt = fcap_misc_string;
	while(token_start_pt = strstr(token_start_pt, token_start)){
		token_start_pt++;

		token_end_pt = strstr(token_start_pt, token_end);
		if(!token_end_pt){
			memcpy(buffer,token_start_pt,sizeof(buffer));
			buffer[sizeof(buffer)-1]=0;
			printk("fcap_misc_run_7113: <%s> error!\n", buffer);
			goto fail2;
		}
		token_length = token_end_pt - token_start_pt;
		memset(buffer, 0 , sizeof(buffer));
		if(token_length>sizeof(buffer))
			memcpy(buffer,token_start_pt,sizeof(buffer)-1);
		else
			memcpy(buffer,token_start_pt,token_length);
		/* check tokens */
		if(strspn(buffer, token_check) != token_length ){
			printk("fcap_misc_run_7113: <%s> error!\n", buffer);
			goto fail2;
		}

		argc = substring(argv, buffer, " r\n\t");
		if (argc > 0)	// has data
		{
			// change token[] to tokenvalue[]
			printk(KERN_DEBUG);
			for (j=0; j<argc; ++j)
			{
				tokenvalue[j] = (unsigned char)atoi(argv[j]);
			printk("0x%02x ",tokenvalue[j]);
			}
			printk("\n");
			if (ExecuteCommand(tokenvalue, argc) < 0)
				return -1;
		}			
		token_start_pt = token_end_pt;
	}
//printk("<%d, %s, %d>\n",retval, fcap_misc_string, filePoint->f_pos);
	retval = 0;

fail2:
	set_fs(old_fs);
fail1:
	if(fcap_misc_string)
		kfree(fcap_misc_string);
	filp_close(filePoint, NULL);
	return retval;	
}

